package intermediately

import (
	"errors"
	"github.com/hyperledger/fabric-protos-go/common"
	pb "github.com/hyperledger/fabric-protos-go/peer"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/channel"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/resmgmt"
	"github.com/hyperledger/fabric-sdk-go/pkg/common/errors/retry"
	"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
	lcpackager "github.com/hyperledger/fabric-sdk-go/pkg/fab/ccpackager/lifecycle"
	"github.com/hyperledger/fabric-sdk-go/pkg/fabsdk"
	"github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/common/policydsl"
)

type ResourceManagement struct {
	context *context
	client  *resmgmt.Client
}

func NewResMgmt() *ResourceManagement {
	return &ResourceManagement{context: NewContext()}
}

// 1 加载context.json,必须要执行
func (receiver *ResourceManagement) LoadContext(contextJsonPath string) *ResourceManagement {
	receiver.context.SetContextJson(contextJsonPath)
	return receiver
}

// 2 加载config.yaml,必须要执行
func (receiver *ResourceManagement) LoadConfig(configYamlPath string) *ResourceManagement {
	receiver.context.SetConfigYaml(configYamlPath)
	return receiver
}

// 3 start Connect
func (receiver *ResourceManagement) Connect() (*ResourceManagement, error) {
	sdk, err := fabsdk.New(receiver.context.ConfigProvider)
	if err != nil {
		return nil, err
	}
	receiver.client, err = resmgmt.New(
		sdk.Context(fabsdk.WithUser(receiver.context.UserName),
			fabsdk.WithOrg(receiver.context.Org)))
	if err != nil {
		return nil, err
	}
	return receiver, nil
}

func (receiver *ResourceManagement) abc() {
	//	receiver.client.QueryChannels()
	//	receiver.client.QueryConfigBlockFromOrderer()
	//	receiver.client.QueryInstalledChaincodes()
	//  receiver.client.QueryInstantiatedChaincodes()
	//  receiver.client.QueryCollectionsConfig()
	//  receiver.client.QueryConfigFromOrderer()
	//	receiver.client.LifecycleQueryInstalledCC()
	//	receiver.client.LifecycleQueryCommittedCC()
	//	receiver.client.LifecycleQueryApprovedCC()
	//  receiver.client.LifecycleCheckCCCommitReadiness()

}

// 参数可以为空，如果是空，默认查询链接的peer
func (receiver *ResourceManagement) QueryChannels(hostNames ...string) (*pb.ChannelQueryResponse, error) {
	if len(hostNames) == 0 {
		peerChannelResp, err := receiver.client.QueryChannels(resmgmt.WithTargetEndpoints(receiver.context.Peer + ":7051"))
		if err != nil {
			return nil, err
		}
		//jsonStr, err := json.Marshal(peerChannelResp)
		//if err != nil {
		//	return "{}", err
		//}
		return peerChannelResp, nil
	} else {
		peerChannelResp, err := receiver.client.QueryChannels(resmgmt.WithTargetEndpoints(hostNames...))
		if err != nil {
			return nil, err
		}
		//jsonStr, err := json.Marshal(peerChannelResp)
		//if err != nil {
		//	return "{}", err
		//}
		return peerChannelResp, nil
	}
}

func (receiver *ResourceManagement) QueryConfigBlockFromOrderer() (*common.Block, error) {
	configBlock, err := receiver.client.QueryConfigBlockFromOrderer(receiver.context.ChannelID,
		resmgmt.WithOrdererEndpoint(receiver.context.Orderer))
	if err != nil {
		return nil, nil
	}
	//jsonStr, err := json.Marshal(configBlock)
	//if err != nil {
	//	return "{}", nil
	//}
	return configBlock, nil
}

// 参数可以为空，如果是空，默认查询链接的peer
func (receiver *ResourceManagement) QueryInstalledChaincodes(hostNames ...string) (*pb.ChaincodeQueryResponse, error) {
	if len(hostNames) == 0 {
		chaincodeQueryResp, err := receiver.client.QueryInstalledChaincodes(resmgmt.WithTargetEndpoints(receiver.context.Peer + ":7051"))
		if err != nil {
			return nil, err
		}
		//jsonStr, err := json.Marshal(chaincodeQueryResp)
		//if err != nil {
		//	return "{}", err
		//}
		return chaincodeQueryResp, nil
	} else {
		chaincodeQueryResp, err := receiver.client.QueryInstalledChaincodes(resmgmt.WithTargetEndpoints(hostNames...))
		if err != nil {
			return nil, err
		}
		//jsonStr, err := json.Marshal(chaincodeQueryResp)
		//if err != nil {
		//	return "{}", err
		//}
		return chaincodeQueryResp, nil
	}

}

func (receiver *ResourceManagement) QueryInstantiatedChaincodes() (*pb.ChaincodeQueryResponse, error) {

	chaincodeQueryResp, err := receiver.client.QueryInstantiatedChaincodes(receiver.context.ChannelID)
	if err != nil {
		return nil, err
	}
	//jsonStr, err := json.Marshal(chaincodeQueryResp)
	//if err != nil {
	//	return "{}", err
	//}
	return chaincodeQueryResp, nil
}

// collections config not defined for chaincode ${name} ,
// 未为链码定义集合配置会报错
func (receiver *ResourceManagement) QueryCollectionsConfig(chaincodeName string) (*pb.CollectionConfigPackage, error) {
	collectionConfigPackage, err := receiver.client.QueryCollectionsConfig(receiver.context.ChannelID, chaincodeName)
	if err != nil {
		return nil, err
	}
	//jsonStr, err := json.Marshal(collectionConfigPackage)
	//if err != nil {
	//	return "{}", nil
	//}
	return collectionConfigPackage, nil
}

func (receiver *ResourceManagement) QueryConfigFromOrderer() (fab.ChannelCfg, error) {
	channelCfg, err := receiver.client.QueryConfigFromOrderer(receiver.context.ChannelID)
	if err != nil {
		return nil, err
	}
	//jsonStr, err := json.Marshal(channelCfg)
	//if err != nil {
	//	return "{}", err
	//}
	return channelCfg, nil
}

/////////////////////////Lifecycle////////////////////////////Lifecycle/////////////////////////////

func (receiver *ResourceManagement) LifecycleQueryInstalledCC(hostNames ...string) ([]resmgmt.LifecycleInstalledCC, error) {
	if len(hostNames) == 0 {
		lifecycleInstallCC, err := receiver.client.LifecycleQueryInstalledCC(resmgmt.WithTargetEndpoints(receiver.context.Peer + ":7051"))
		if err != nil {
			return nil, err
		}
		//jsonStr, err := json.Marshal(lifecycleInstallCC)
		//if err != nil {
		//	return "{}", err
		//}
		return lifecycleInstallCC, nil
	} else {
		lifecycleInstallCC, err := receiver.client.LifecycleQueryInstalledCC(resmgmt.WithTargetEndpoints(hostNames...))
		if err != nil {
			return nil, err
		}
		//jsonStr, err := json.Marshal(lifecycleInstallCC)
		//if err != nil {
		//	return "{}", err
		//}
		return lifecycleInstallCC, nil
	}

}

// Request 需要结构体内有一个Name字段，是链码命令空间，
// 很显然我们在安装的时候没有涉及命名空间，因此不需要填写，
// 后面的选项是目标节点，只能是orderer或者不加自动访问orderer
func (receiver *ResourceManagement) LifecycleQueryCommittedCC(nameSpace string) ([]resmgmt.LifecycleChaincodeDefinition, error) {
	liArray, err := receiver.client.LifecycleQueryCommittedCC(receiver.context.ChannelID,
		resmgmt.LifecycleQueryCommittedCCRequest{Name: nameSpace})
	if err != nil {
		return nil, err
	}
	//jsonStr, err := json.Marshal(liArray)
	//if err != nil {
	//	return "{}", err
	//}
	return liArray, nil
}

func (receiver *ResourceManagement) LifecycleQueryApprovedCC(chaincodeName string, sequence int64, hostNames ...string) (resmgmt.LifecycleApprovedChaincodeDefinition, error) {
	lifecycleACD, err := receiver.client.LifecycleQueryApprovedCC(
		receiver.context.ChannelID,
		resmgmt.LifecycleQueryApprovedCCRequest{
			Name:     chaincodeName,
			Sequence: sequence,
		},
		resmgmt.WithTargetEndpoints(hostNames...))
	if err != nil {
		return resmgmt.LifecycleApprovedChaincodeDefinition{}, err
	}
	//jsonStr, err := json.Marshal(lifecycleACD)
	//if err != nil {
	//	return "{}", err
	//}
	return lifecycleACD, nil
}

func (receiver *ResourceManagement) LifecycleCheckCCCommitReadiness(chaincodeName string, sequence int64) (resmgmt.LifecycleCheckCCCommitReadinessResponse, error) {

	liCkCCComResp, err := receiver.client.LifecycleCheckCCCommitReadiness(
		receiver.context.ChannelID,
		resmgmt.LifecycleCheckCCCommitReadinessRequest{
			Name:     chaincodeName,
			Version:  "1",
			Sequence: sequence},
	)
	if err != nil {
		return resmgmt.LifecycleCheckCCCommitReadinessResponse{}, err
	}
	//jsonStr, err := json.Marshal(liCkCCComResp)
	//if err != nil {
	//	return "{}", err
	//}
	return liCkCCComResp, nil

}

func (receiver *ResourceManagement) LifecycleChaincodeOnceOk(
	channelInter *Channel,
	signatureMspID string,
	chaincodePath, chaincodeName, label, version, initFunc string,
	isInit bool,
	sequence int64,
	chaincodeType pb.ChaincodeSpec_Type,
	args ...string,
) ([]fab.TransactionID, channel.Response, error) {
	var transactionIdArray []fab.TransactionID
	ccPolicy := SetCCPolicy(signatureMspID)
	descriptor, ccPkg, err := PackageCC(chaincodePath, label, chaincodeType)
	if err != nil {
		return nil, channel.Response{}, err
	}
	packageID, err := InstallCC(descriptor.Label, ccPkg, receiver.client)
	if err != nil {
		return nil, channel.Response{}, err
	}
	fabTsId0, err := ApproveCC(
		receiver.context.Orderer,
		receiver.context.Peer,
		receiver.context.ChannelID,
		packageID,
		chaincodeName,
		version,
		sequence,
		ccPolicy,
		receiver.client)
	if err != nil {
		return nil, channel.Response{}, err
	}
	transactionIdArray = append(transactionIdArray, fabTsId0)
	fabTsId1, err := CommitCC(
		receiver.context.Orderer,
		receiver.context.Peer,
		receiver.context.ChannelID,
		packageID,
		chaincodeName,
		version,
		sequence,
		ccPolicy,
		receiver.client,
	)
	if err != nil {
		return nil, channel.Response{}, err
	}
	transactionIdArray = append(transactionIdArray, fabTsId1)
	resp, err := InitCC(
		channelInter,
		chaincodeName,
		initFunc,
		isInit,
		args...,
	)
	if err != nil {
		return nil, channel.Response{}, err
	}

	return transactionIdArray, resp, nil
}

///////////////Lifecycle///////////////chaincode///////////////install///////////////....///////////////
func SetCCPolicy(memberMspID string) *common.SignaturePolicyEnvelope {
	return policydsl.SignedByAnyMember([]string{memberMspID})
}

func PackageCC(chaincodePath, label string, chaincodeType pb.ChaincodeSpec_Type) (*lcpackager.Descriptor, []byte, error) {
	desc := &lcpackager.Descriptor{
		Path:  chaincodePath,
		Type:  chaincodeType,
		Label: label,
	}
	ccPkg, err := lcpackager.NewCCPackage(desc)
	if err != nil {
		return nil, nil, err
	}
	return desc, ccPkg, nil
}

func InstallCC(label string, ccPkg []byte, orgResMgmt *resmgmt.Client) (string, error) {
	installCCReq := resmgmt.LifecycleInstallCCRequest{
		Label:   label,
		Package: ccPkg,
	}

	packageID := lcpackager.ComputePackageID(installCCReq.Label, installCCReq.Package)

	resp, err := orgResMgmt.LifecycleInstallCC(installCCReq, resmgmt.WithRetry(retry.DefaultResMgmtOpts))
	if err != nil {
		return "", err
	}
	if packageID == resp[0].PackageID {
		return packageID, nil
	} else {
		return "", errors.New("打包链码 package_id 和安装链码返回的 package_id 不相同")
	}
}

func ApproveCC(ordererHostName, peerHostName, channelName, packageID, chaincodeName, version string, sequence int64, ccPolicy *common.SignaturePolicyEnvelope, orgResMgmt *resmgmt.Client) (fab.TransactionID, error) {
	approveCCReq := resmgmt.LifecycleApproveCCRequest{
		Name:              chaincodeName,
		Version:           version,
		PackageID:         packageID,
		Sequence:          sequence,
		EndorsementPlugin: "escc",
		ValidationPlugin:  "vscc",
		SignaturePolicy:   ccPolicy,
		InitRequired:      true,
	}
	txnID, err := orgResMgmt.LifecycleApproveCC(
		channelName, approveCCReq,
		resmgmt.WithTargetEndpoints(peerHostName),
		resmgmt.WithOrdererEndpoint(ordererHostName),
		resmgmt.WithRetry(retry.DefaultResMgmtOpts))
	if err != nil {
		return "", err
	}
	return txnID, nil
}

func CommitCC(ordererHostName, peerHostName, channelName, packageID, chaincodeName, version string, sequence int64, ccPolicy *common.SignaturePolicyEnvelope, orgResMgmt *resmgmt.Client) (fab.TransactionID, error) {
	req := resmgmt.LifecycleCommitCCRequest{
		Name:              chaincodeName,
		Version:           version,
		Sequence:          1,
		EndorsementPlugin: "escc",
		ValidationPlugin:  "vscc",
		SignaturePolicy:   ccPolicy,
		InitRequired:      true,
	}
	txnID, err := orgResMgmt.LifecycleCommitCC(
		channelName, req, resmgmt.WithRetry(retry.DefaultResMgmtOpts),
		resmgmt.WithTargetEndpoints(peerHostName),
		resmgmt.WithOrdererEndpoint(ordererHostName))
	if err != nil {
		return "", err
	}
	return txnID, nil
}

func InitCC(channelInter *Channel, chaincodeName, initFunc string, isInit bool, args ...string) (channel.Response, error) {
	var defaultQueryArgs [][]byte
	for _, arg := range args {
		defaultQueryArgs = append(defaultQueryArgs, []byte(arg))
	}
	// init
	resp, err := channelInter.client.Execute(
		channel.Request{
			ChaincodeID: chaincodeName,
			Fcn:         initFunc,
			Args:        defaultQueryArgs,
			IsInit:      isInit},
		channel.WithRetry(retry.DefaultChannelOpts))
	if err != nil {
		return channel.Response{}, err
	}
	return resp, nil
}

type OnceInstallOkResp struct {
	TransactionID []fab.TransactionID `json:"transaction_id"`
	Load          channel.Response    `json:"load"`
}
